<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="shell 命令行参数（基本）">
<meta name="twitter:description" content="<p>命令行参数：位置参数，脚本名，测试参数，获取所有参数；移动变量shift：使用示例，移动多个位置；处理选项：case配合shift，数组带空格的问题，分离参数和选项</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/shell-command-line-parameters-basic.html"/>
<meta property="og:title" content="来玩魔王的咚 | shell 命令行参数（基本）" />
<meta property="og:description" content="<p>命令行参数：位置参数，脚本名，测试参数，获取所有参数；移动变量shift：使用示例，移动多个位置；处理选项：case配合shift，数组带空格的问题，分离参数和选项</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>命令行参数：位置参数，脚本名，测试参数，获取所有参数；移动变量shift：使用示例，移动多个位置；处理选项：case配合shift，数组带空格的问题，分离参数和选项</p>" />

    <meta name="tags" content="shell" />
    <meta name="tags" content="命令行参数" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li class="active"><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-09-02 15:00:00+08:00">
                <script>document.write(moment('2020-09-02 15:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-09-02 15:00:00+08:00">
                <script>document.write(moment('2020-09-02 15:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            shell 命令行参数（基本）
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#ming-ling-xing-can-shu">命令行参数</a><ul>
<li><a href="#wei-zhi-can-shu">位置参数</a></li>
<li><a href="#huo-qu-jiao-ben-ming">获取脚本名</a></li>
<li><a href="#ce-shi-can-shu">测试参数</a></li>
<li><a href="#huo-qu-suo-you-can-shu">获取所有参数</a></li>
</ul>
</li>
<li><a href="#yi-dong-bian-liang-shift">移动变量 shift</a><ul>
<li><a href="#shi-yong-shi-li">使用示例</a></li>
<li><a href="#yi-dong-duo-ge-wei-zhi">移动多个位置</a></li>
</ul>
</li>
<li><a href="#chu-li-xuan-xiang">处理选项</a><ul>
<li><a href="#case-pei-he-shift">case 配合 shift</a></li>
<li><a href="#fen-li-can-shu-he-xuan-xiang">分离参数和选项</a></li>
</ul>
</li>
</ul>
</div>
<h3 id="ming-ling-xing-can-shu"><a class="toclink" href="#ming-ling-xing-can-shu">命令行参数</a></h3>
<p>$0 表示程序名。<br>
$1 至 $9则是位置参数。<br>
$# 表示参数的个数。<br>
$* 将所有参数当做一个整体来引用
$@ 把每个参数作为一个字符串返回，可以使用for循环来遍历
$? 最近一个执行的命令的退出状态。0表示执行成功
$_ 上一个命令的最后一个参数。使用快捷键 ESC+. 也是这个效果</p>
<h4 id="wei-zhi-can-shu"><a class="toclink" href="#wei-zhi-can-shu">位置参数</a></h4>
<p>位置参数不止9个，更多的参数也是一样支持的。只是要使用${10}这样的形式引用。<br>
$1 和 ${1}的效果是一样的。<br>
不用花括号的话，$10 会被认为是 $1 和一个字符 0。  </p>
<p><strong>带空格的参数值</strong><br>
每个参数都是用空格分隔的。要在参数值中包含空格，必须要使用引号（单引号或双引号都可）。  </p>
<blockquote>
<p>将文本字符串作为参数传递时，引号并非数据的一部分。它们只是表明数据的起止位置。  </p>
</blockquote>
<h4 id="huo-qu-jiao-ben-ming"><a class="toclink" href="#huo-qu-jiao-ben-ming">获取脚本名</a></h4>
<p>$0 表示脚本名，但是不同的调用方法返回的结果也是不同的。下面的脚本就是简单的打印$0的值：</p>
<div class="highlight"><pre><span></span><code>$ cat filename.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> <span class="nv">$0</span>
$ ./filename.sh
./filename.sh
$ bash filename.sh 
filename.sh
$ bash /root/filename.sh 
/root/filename.sh
$ 
</code></pre></div>

<p>不通的调用方式会导致参数值的不同。差别是前的路径，最后的文件名是一样的。需要下面的技巧来解决。  </p>
<p><strong>使用 basename 命令</strong><br>
如果要使用脚本名称来进行判断，可以先用命令 basename 把路径的信息给过滤掉。命令的效果如下：</p>
<div class="highlight"><pre><span></span><code>$ basename /var/log/messages 
messages
$ 
</code></pre></div>

<p>所以上面的脚本可以修改成这样：</p>
<div class="highlight"><pre><span></span><code>$ cat filename.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> <span class="k">$(</span>basename <span class="nv">$0</span><span class="k">)</span>
$ ./filename.sh
filename.sh
$ bash filename.sh 
filename.sh
$ bash /root/filename.sh 
filename.sh
$ 
</code></pre></div>

<h4 id="ce-shi-can-shu"><a class="toclink" href="#ce-shi-can-shu">测试参数</a></h4>
<p>在脚本中使用参数要确保参数存在，否则运行时有可能会报错：</p>
<div class="highlight"><pre><span></span><code>$ cat add.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> <span class="nv">$1</span> + <span class="nv">$2</span> <span class="o">=</span> $<span class="o">[</span> <span class="nv">$1</span> + <span class="nv">$2</span> <span class="o">]</span>
$ ./add.sh <span class="m">1</span> <span class="m">2</span>
<span class="m">1</span> + <span class="nv">2</span> <span class="o">=</span> <span class="m">3</span>
$ ./add.sh <span class="m">1</span>
./add.sh:行2: <span class="m">1</span> +  : 语法错误: 期待操作数 （错误符号是 <span class="s2">&quot;+  &quot;</span>）
$ 
</code></pre></div>

<p>如果只是当做字符串引用，也不会报错。没有传参的参数默认都是空：</p>
<div class="highlight"><pre><span></span><code>$ cat hello.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> Hello <span class="nv">$1</span> <span class="nv">$2</span>.
$ ./hello.sh Tom Jerry
Hello Tom Jerry.
$ ./hello.sh Jerry
Hello Jerry .
$ ./hello.sh
Hello .
$ 
</code></pre></div>

<h5 id="pan-duan-can-shu-shi-fou-cun-zai"><a class="toclink" href="#pan-duan-can-shu-shi-fou-cun-zai">判断参数是否存在</a></h5>
<p>在 shell 中利用 -n 来判定字符串非空，-z 则正好相反，空即是真。上面已经测试过了，未定义的参数默认是空：</p>
<div class="highlight"><pre><span></span><code>$ cat hello.sh 
<span class="c1">#!/bin/bash</span>
<span class="k">if</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="o">]</span>
<span class="k">then</span>
    <span class="nb">echo</span> Hello <span class="nv">$1</span>.
<span class="k">else</span>
    <span class="nb">echo</span> Hello Nobody.
<span class="k">fi</span>
$ ./hello.sh Tom
Hello Tom.
$ ./hello.sh
Hello Nobody.
$ 
</code></pre></div>

<p>这里的判断的 $1 要加上双引号，否则会被认为是字符串。一个字符串当然非空，所以结果会永远为真。  </p>
<h5 id="pan-duan-can-shu-de-ge-shu"><a class="toclink" href="#pan-duan-can-shu-de-ge-shu">判断参数的个数</a></h5>
<p>上面的例子的脚本也可以通过判断参数数量是否大于0来实现：</p>
<div class="highlight"><pre><span></span><code>$ cat hello.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> 参数数量: <span class="nv">$#</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$#</span> -gt <span class="m">0</span> <span class="o">]</span>
<span class="k">then</span>
    <span class="nb">echo</span> Hello <span class="nv">$1</span>.
<span class="k">else</span>
    <span class="nb">echo</span> Hello Nobody.
<span class="k">fi</span>
$ ./hello.sh
参数数量: <span class="m">0</span>
Hello Nobody.
$ ./hello.sh Tom
参数数量: <span class="m">1</span>
Hello Tom.
$ ./hello.sh Tom Jerry
参数数量: <span class="m">2</span>
Hello Tom.
$ 
</code></pre></div>

<p>这里 -gt 比较的是前后两个数字（INT），所以$#是不加引号的。<br>
用这种方法也能判断参数是否存在。两种方法，效果一样，不同的书上都看到有人使用。  </p>
<p>这里是一样加法的例子，必须要传入2个参数：</p>
<div class="highlight"><pre><span></span><code>$ cat add.sh 
<span class="c1">#!/bin/bash</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$#</span> -eq <span class="m">2</span> <span class="o">]</span>
<span class="k">then</span>
    <span class="nb">echo</span> <span class="nv">$1</span> + <span class="nv">$2</span> <span class="o">=</span> $<span class="o">[</span> <span class="nv">$1</span> + <span class="nv">$2</span> <span class="o">]</span>
<span class="k">else</span>
    <span class="nb">echo</span> 需要参数: <span class="m">2</span>, 实际参数: <span class="nv">$#</span>.
<span class="k">fi</span>
$ ./add.sh <span class="m">1</span> <span class="m">2</span>
<span class="m">1</span> + <span class="nv">2</span> <span class="o">=</span> <span class="m">3</span>
$ ./add.sh <span class="m">1</span> <span class="m">2</span> <span class="m">3</span>
需要参数: <span class="m">2</span>, 实际参数: <span class="m">3</span>.
$ ./add.sh <span class="m">1</span>
需要参数: <span class="m">2</span>, 实际参数: <span class="m">1</span>.
$ 
</code></pre></div>

<p>如果要表示不相等，就是 <code>if [ $# -ne 2 ]</code>。  </p>
<h5 id="huo-qu-zui-hou-yi-ge-can-shu"><a class="toclink" href="#huo-qu-zui-hou-yi-ge-can-shu">获取最后一个参数</a></h5>
<p>这是一个使用 $# 的小技巧。使用<code>${$#}</code>似乎就是参数的最后一个变量了。<br>
但是其实不然，花括号里不能这样用$，这里要把里面的换成感叹号：</p>
<div class="highlight"><pre><span></span><code>$ cat hello.sh 
<span class="c1">#!/bin/bash</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$#</span> -gt <span class="m">0</span> <span class="o">]</span>
<span class="k">then</span>
    <span class="nb">echo</span> Hello <span class="si">${</span><span class="p">!#</span><span class="si">}</span>.
<span class="k">else</span>
    <span class="nb">echo</span> Hello Nobody.
<span class="k">fi</span>
$ ./hello.sh Tom Jerry
Hello Jerry.
$ 
</code></pre></div>

<p>如果没有任何命令行参数，那么就是返回$0，也就是脚本名。  </p>
<p>上面感叹号的问题，效果是引用变量的值而不是变量自身。类似于指针的取值。把#号换成一个有名字的变量来说明比较直观：</p>
<div class="highlight"><pre><span></span><code>$ cat parameter.sh 
<span class="c1">#!/bin/bash</span>
<span class="nv">paramater</span><span class="o">=</span>key
<span class="nv">key</span><span class="o">=</span>value
<span class="nb">echo</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">paramater</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nb">echo</span> <span class="s2">&quot;</span><span class="si">${</span><span class="p">!paramater</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="nb">echo</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">key</span><span class="si">}</span><span class="s2">&quot;</span>
$ ./parameter.sh 
key
value
value
$ 
</code></pre></div>

<p>不加感叹号，就是直接去该变量的值。加上感叹号，就是去变量值所对应的变量名的那个变量的值。  </p>
<h4 id="huo-qu-suo-you-can-shu"><a class="toclink" href="#huo-qu-suo-you-can-shu">获取所有参数</a></h4>
<p>$* 和 $@ 都是表示所有的字符串，但是在遍历的时候会有区别：</p>
<div class="highlight"><pre><span></span><code>$ cat all.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> <span class="s1">&#39;$* 的效果:&#39;</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">for</span> i <span class="k">in</span> <span class="s2">&quot;</span><span class="nv">$*</span><span class="s2">&quot;</span>
<span class="k">do</span>
    <span class="nb">echo</span> <span class="nv">$count</span>: <span class="nv">$i</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
<span class="nb">echo</span> <span class="s1">&#39;$@ 的效果:&#39;</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">for</span> i <span class="k">in</span> <span class="s2">&quot;</span><span class="nv">$@</span><span class="s2">&quot;</span>
<span class="k">do</span>
    <span class="nb">echo</span> <span class="nv">$count</span>: <span class="nv">$i</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
$ ./all.sh Oliver Barry Kara Sara Kane
<span class="nv">$*</span> 的效果:
<span class="m">1</span>: Oliver Barry Kara Sara Kane
<span class="nv">$@</span> 的效果:
<span class="m">1</span>: Oliver
<span class="m">2</span>: Barry
<span class="m">3</span>: Kara
<span class="m">4</span>: Sara
<span class="m">5</span>: Kane
$ 
</code></pre></div>

<p>$*就一个整体的值，无法遍历。要遍历每一个变量要使用$@。这里的双引号很重要。  </p>
<p>不加引号的话，就是把 $* 和 $@ 的内容（变量解析后就是多个词）传递给for循环遍历，这样两个参数的效果是一样的。和直接传不加引号的字符串的效果一样。<br>
加上引号，引号里的内容就是一个整体。如果是$*，这个整体里的所有内容还是一个词，不会拆。如果是$@，这个整体里会按空格拆分成多个词。<br>
下面是演示的效果：</p>
<div class="highlight"><pre><span></span><code>$ cat all2.sh 
<span class="c1">#!/bin/bash</span>
<span class="nb">echo</span> <span class="s1">&#39;$* 不加引号的效果:&#39;</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">for</span> i <span class="k">in</span> <span class="nv">$*</span>
<span class="k">do</span>
    <span class="nb">echo</span> <span class="nv">$count</span>: <span class="nv">$i</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
<span class="nb">echo</span> <span class="s1">&#39;$@ 不加引号的效果:&#39;</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">for</span> i <span class="k">in</span> <span class="nv">$@</span>
<span class="k">do</span>
    <span class="nb">echo</span> <span class="nv">$count</span>: <span class="nv">$i</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
<span class="nb">echo</span> <span class="s1">&#39;直接遍历不加引号的字符的效果:&#39;</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">for</span> i <span class="k">in</span> Oliver Barry Kara Sara Kane
<span class="k">do</span>
    <span class="nb">echo</span> <span class="nv">$count</span>: <span class="nv">$i</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
<span class="nb">echo</span> <span class="s1">&#39;加引号遍历的效果:&#39;</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">for</span> i <span class="k">in</span> <span class="s2">&quot;Oliver Barry Kara Sara Kane&quot;</span>
<span class="k">do</span>
    <span class="nb">echo</span> <span class="nv">$count</span>: <span class="nv">$i</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
$ ./all2.sh Oliver Barry Kara Sara Kane
<span class="nv">$*</span> 不加引号的效果:
<span class="m">1</span>: Oliver
<span class="m">2</span>: Barry
<span class="m">3</span>: Kara
<span class="m">4</span>: Sara
<span class="m">5</span>: Kane
<span class="nv">$@</span> 不加引号的效果:
<span class="m">1</span>: Oliver
<span class="m">2</span>: Barry
<span class="m">3</span>: Kara
<span class="m">4</span>: Sara
<span class="m">5</span>: Kane
直接遍历不加引号的字符的效果:
<span class="m">1</span>: Oliver
<span class="m">2</span>: Barry
<span class="m">3</span>: Kara
<span class="m">4</span>: Sara
<span class="m">5</span>: Kane
加引号遍历的效果:
<span class="m">1</span>: Oliver Barry Kara Sara Kane
$ 
</code></pre></div>

<p><strong>强调：</strong>特殊参数$@一定要用在双引号内，效果是每个参数都扩展为分隔的单词。在使用for循环遍历的时候会体现出效果。  </p>
<h3 id="yi-dong-bian-liang-shift"><a class="toclink" href="#yi-dong-bian-liang-shift">移动变量 shift</a></h3>
<p>shift 命令能够用来操作命令行参数。默认情况下将每个参数向左移动一个位置。被移出的参数就被丢弃了，无法恢复。<br>
先掌握这个命令的使用，使用这个命令可以方便地解析命令行参数。  </p>
<h4 id="shi-yong-shi-li"><a class="toclink" href="#shi-yong-shi-li">使用示例</a></h4>
<p>下面是一个简单的示例：</p>
<div class="highlight"><pre><span></span><code>$ cat pop.sh 
<span class="c1">#!/bin/bash</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="k">while</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="o">]</span>
<span class="c1"># while [ $# -ne 0 ]</span>
<span class="k">do</span>
    <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$count</span><span class="s2">: </span><span class="nv">$1</span><span class="s2">&quot;</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
    <span class="nb">shift</span>
<span class="k">done</span>
$ ./pop.sh Oliver Barry Kara Sara Kane
<span class="m">1</span>: Oliver
<span class="m">2</span>: Barry
<span class="m">3</span>: Kara
<span class="m">4</span>: Sara
<span class="m">5</span>: Kane
$ 
</code></pre></div>

<p>这里有2中判断方法来判断是否还有参数，效果是一样的。  </p>
<h4 id="yi-dong-duo-ge-wei-zhi"><a class="toclink" href="#yi-dong-duo-ge-wei-zhi">移动多个位置</a></h4>
<p>带参数执行shift，指明要移动几个位置就可以了：</p>
<div class="highlight"><pre><span></span><code>$ cat pop.sh 
<span class="c1">#!/bin/bash</span>
<span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="c1"># while [ -n &quot;$1&quot; ]</span>
<span class="k">while</span> <span class="o">[</span> <span class="nv">$#</span> -ne <span class="m">0</span> <span class="o">]</span>
<span class="k">do</span>
    <span class="k">if</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span> <span class="o">]</span>
    <span class="k">then</span>
        <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$count</span><span class="s2">: </span><span class="nv">$1</span><span class="s2">, </span><span class="nv">$2</span><span class="s2">&quot;</span>
        <span class="nb">shift</span> <span class="m">2</span>
    <span class="k">else</span>
        <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$count</span><span class="s2">: </span><span class="nv">$1</span><span class="s2">&quot;</span>
        <span class="nb">shift</span>
    <span class="k">fi</span>
    <span class="nv">count</span><span class="o">=</span>$<span class="o">[</span> <span class="nv">$count</span> + <span class="m">1</span> <span class="o">]</span>
<span class="k">done</span>
$ ./pop.sh Oliver Barry Kara Sara Kane
<span class="m">1</span>: Oliver, Barry
<span class="m">2</span>: Kara, Sara
<span class="m">3</span>: Kane
$ 
</code></pre></div>

<p>简单修改下上面的脚本，一次输出2个参数，然后移动2个位置。  </p>
<h3 id="chu-li-xuan-xiang"><a class="toclink" href="#chu-li-xuan-xiang">处理选项</a></h3>
<p>当shell脚本需要多个命令行参数时，在调用脚本的时候就必须将所有参数按固定的顺序书写。（类似python函数的位置参数）<br>
或者还可以使用选项来指定参数的值。（类似python函数的关键字参数）  </p>
<h4 id="case-pei-he-shift"><a class="toclink" href="#case-pei-he-shift">case 配合 shift</a></h4>
<p>这个例子里有带值的选项也有不带值的选项：</p>
<div class="highlight"><pre><span></span><code>$ cat format.sh 
<span class="c1">#!/bin/bash</span>
<span class="nv">prefix</span><span class="o">=</span><span class="s2">&quot;&quot;</span>    <span class="c1"># 前缀</span>
<span class="nv">base</span><span class="o">=</span><span class="s2">&quot;test&quot;</span>  <span class="c1"># 默认字符串</span>
<span class="nv">suffix</span><span class="o">=</span><span class="s2">&quot;&quot;</span>    <span class="c1"># 后缀</span>
<span class="nv">upper</span><span class="o">=</span>off    <span class="c1"># 是否大写</span>
<span class="c1"># 解析命令行参数</span>
<span class="k">while</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="o">]</span>
<span class="k">do</span>
    <span class="k">case</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="k">in</span>
        -a<span class="o">)</span> <span class="nv">suffix</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -b<span class="o">)</span> <span class="nv">prefix</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -s<span class="o">)</span> <span class="nv">base</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -u<span class="o">)</span> <span class="nv">upper</span><span class="o">=</span>on <span class="p">;;</span>
         *<span class="o">)</span> <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2"> is not an option&quot;</span>
            <span class="nb">exit</span> <span class="m">1</span> <span class="p">;;</span>  <span class="c1"># 发现未知参数，直接退出</span>
    <span class="k">esac</span>
    <span class="nb">shift</span>
<span class="k">done</span>
<span class="c1"># 添加前缀和后缀</span>
<span class="nv">output</span><span class="o">=</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">prefix</span><span class="p">:+</span><span class="si">${</span><span class="nv">prefix</span><span class="si">}</span><span class="nv">_</span><span class="si">}${</span><span class="nv">base</span><span class="si">}${</span><span class="nv">suffix</span><span class="p">:+_</span><span class="si">${</span><span class="nv">suffix</span><span class="si">}}</span><span class="s2">&quot;</span>
<span class="c1"># 判断是否要全大写输出</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$upper</span> <span class="o">=</span> on <span class="o">]</span>
<span class="k">then</span>
    <span class="nv">output</span><span class="o">=</span><span class="si">${</span><span class="nv">output</span><span class="p">^^</span><span class="si">}</span>
<span class="k">fi</span>
<span class="c1"># 输出结果</span>
<span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$output</span><span class="s2">&quot;</span>
$ ./format.sh -a after
test_after
$ ./format.sh -s hello -b befor
befor_hello
$ ./format.sh -s hello -u -a after -b befor
BEFOR_HELLO_AFTER
$ ./format.sh -s hello -u -a after -b befor -l
-l is not an option
$ 
</code></pre></div>

<p>case语句找到一个选项就处理一个选项。如果还需要在命令行提供其他参数，可以在通用情况的处理部分中处理。而这里因为不需要提供任何参数，凡是解析不正确的就报告错误并退出（exit 1）。  </p>
<h5 id="neng-jie-xi-can-shu-de-ban-ben"><a class="toclink" href="#neng-jie-xi-can-shu-de-ban-ben">能解析参数的版本</a></h5>
<p>这个版本匹配所有的参数进行格式化输出：</p>
<div class="highlight"><pre><span></span><code>$ cat format.sh 
<span class="c1">#!/bin/bash</span>
<span class="nv">prefix</span><span class="o">=</span><span class="s2">&quot;&quot;</span>    <span class="c1"># 前缀</span>
<span class="nv">base</span><span class="o">=</span><span class="s2">&quot;test&quot;</span>  <span class="c1"># 默认字符串</span>
<span class="nv">suffix</span><span class="o">=</span><span class="s2">&quot;&quot;</span>    <span class="c1"># 后缀</span>
<span class="nv">upper</span><span class="o">=</span>off    <span class="c1"># 是否大写</span>
<span class="c1"># 显示声明一下这是个数组变量，其实没有必要</span>
<span class="nb">declare</span> -a names  <span class="c1"># 需要格式化输出的所有原始字符串</span>
<span class="c1"># 解析命令行参数</span>
<span class="k">while</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="o">]</span>
<span class="k">do</span>
    <span class="k">case</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="k">in</span>
        -a<span class="o">)</span> <span class="nv">suffix</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -b<span class="o">)</span> <span class="nv">prefix</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -s<span class="o">)</span> <span class="nv">base</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -u<span class="o">)</span> <span class="nv">upper</span><span class="o">=</span>on <span class="p">;;</span>
         *<span class="o">)</span> <span class="nv">names</span><span class="o">=(</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">names</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="o">)</span> <span class="p">;;</span>
    <span class="k">esac</span>
    <span class="nb">shift</span>
<span class="k">done</span>
names<span class="o">[</span><span class="m">0</span><span class="o">]=</span><span class="si">${</span><span class="nv">names</span><span class="p">[0]</span><span class="k">:-</span><span class="nv">$base</span><span class="si">}</span>
<span class="k">for</span> name <span class="k">in</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">names</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">do</span>
    <span class="c1"># 添加前缀和后缀</span>
    <span class="nv">output</span><span class="o">=</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">prefix</span><span class="p">:+</span><span class="si">${</span><span class="nv">prefix</span><span class="si">}</span><span class="nv">_</span><span class="si">}${</span><span class="nv">name</span><span class="si">}${</span><span class="nv">suffix</span><span class="p">:+_</span><span class="si">${</span><span class="nv">suffix</span><span class="si">}}</span><span class="s2">&quot;</span>
    <span class="c1"># 判断是否要全大写输出</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$upper</span> <span class="o">=</span> on <span class="o">]</span>
    <span class="k">then</span>
        <span class="nv">output</span><span class="o">=</span><span class="si">${</span><span class="nv">output</span><span class="p">^^</span><span class="si">}</span>
    <span class="k">fi</span>
    <span class="c1"># 输出结果</span>
    <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$output</span><span class="s2">&quot;</span>
<span class="k">done</span>
$ 
$ ./format.sh -a after -b befor -u value1 value2 value3
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after after1 -b befor befor1 -u value1 value2 value3
BEFOR_AFTER1_AFTER
BEFOR_BEFOR1_AFTER
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after after1 -b befor befor1 -u -v value1 value2 value3
BEFOR_AFTER1_AFTER
BEFOR_BEFOR1_AFTER
BEFOR_-V_AFTER
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ 
</code></pre></div>

<p>看最后的两项的结果，提供的命令行参数有问题，但是程序无法发现。<br>
倒数第二项可以认为提供的参数是对的，但是选项和参数交替出现。<br>
而最后一项提供了一个错误的选项，但是无法识别出来。<br>
解决这个问题，需要更加规范的方法来分离参数和选项。下一小节的内容。  </p>
<h5 id="shu-zu-dai-kong-ge-de-wen-ti"><a class="toclink" href="#shu-zu-dai-kong-ge-de-wen-ti">数组带空格的问题</a></h5>
<p>数组添加元素有很多方法，这里是一种重新创建数组的做法：</p>
<div class="highlight"><pre><span></span><code><span class="nv">array_name</span><span class="o">=(</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">array_name</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span> value1 ... valueN<span class="o">)</span>
</code></pre></div>

<p>可以一次添加多个元素，如果字符串包含空格，就要加上引号。  </p>
<p>和命令行参数的$@与$*一样，数组所有的元素也有这两个类似的符号。最严谨的方法是使用 <code>"${names[@]}"</code> 使用带双引号的@。<br>
添加元素和取出元素的时候都要注意，否则存在带空格的元素的时候就会破坏数组原本的元素分隔。<br>
添加元素这里使用：</p>
<div class="highlight"><pre><span></span><code><span class="nv">names</span><span class="o">=(</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">names</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="o">)</span>
</code></pre></div>

<p>不单是数组里的元素，被添加的元素也要加上双引号，否则如果有空格，就会按多个元素被添加进数组。  </p>
<p>遍历元素使用：</p>
<div class="highlight"><pre><span></span><code><span class="k">for</span> name <span class="k">in</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">names</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span>
</code></pre></div>

<p>只有添加的时候正确了，才能正确的遍历。然后遍历的时候也要保证正确。<br>
验证效果：</p>
<div class="highlight"><pre><span></span><code>$ ./format.sh -a after -b befor -u value1 <span class="s2">&quot;value2 value3&quot;</span> value4
BEFOR_VALUE1_AFTER
BEFOR_VALUE2 VALUE3_AFTER
BEFOR_VALUE4_AFTER
$ 
</code></pre></div>

<p>完美。  </p>
<h4 id="fen-li-can-shu-he-xuan-xiang"><a class="toclink" href="#fen-li-can-shu-he-xuan-xiang">分离参数和选项</a></h4>
<p>这里的参数就是命令行参数中除了定义的选项之外，其他额外的参数。要同时处理参数和选项，就要用特殊字符（双破折线--）将二者分开。双破折线表明选项列表结束，双破折线后面的都是参数。基于这个逻辑，只要在case语句中加一项判断就行了。<br>
把上面的脚本做一些修改：</p>
<div class="highlight"><pre><span></span><code>$ cat format.sh 
<span class="c1">#!/bin/bash</span>
<span class="nv">prefix</span><span class="o">=</span><span class="s2">&quot;&quot;</span>    <span class="c1"># 前缀</span>
<span class="nv">base</span><span class="o">=</span><span class="s2">&quot;test&quot;</span>  <span class="c1"># 默认字符串</span>
<span class="nv">suffix</span><span class="o">=</span><span class="s2">&quot;&quot;</span>    <span class="c1"># 后缀</span>
<span class="nv">upper</span><span class="o">=</span>off    <span class="c1"># 是否大写</span>
<span class="c1"># 显示声明一下这是个数组变量，其实没有必要</span>
<span class="nb">declare</span> -a names  <span class="c1"># 需要格式化输出的所有原始字符串</span>
<span class="c1"># 解析选项</span>
<span class="k">while</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="o">]</span>
<span class="k">do</span>
    <span class="k">case</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="k">in</span>
        -a<span class="o">)</span> <span class="nv">suffix</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -b<span class="o">)</span> <span class="nv">prefix</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -s<span class="o">)</span> <span class="nv">base</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span>
            <span class="nb">shift</span> <span class="p">;;</span>
        -u<span class="o">)</span> <span class="nv">upper</span><span class="o">=</span>on <span class="p">;;</span>
        --<span class="o">)</span> <span class="nb">shift</span>
            <span class="nb">break</span> <span class="p">;;</span>
         *<span class="o">)</span> <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2"> is not an option&quot;</span>
            <span class="nb">exit</span> <span class="m">1</span> <span class="p">;;</span>  <span class="c1"># 发现未知参数，直接退出</span>
    <span class="k">esac</span>
    <span class="nb">shift</span>
<span class="k">done</span>
<span class="c1"># 解析参数</span>
<span class="k">while</span> <span class="o">[</span> -n <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="o">]</span>
<span class="k">do</span>
    <span class="nv">names</span><span class="o">=(</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">names</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="o">)</span>
    <span class="nb">shift</span>
<span class="k">done</span>
names<span class="o">[</span><span class="m">0</span><span class="o">]=</span><span class="si">${</span><span class="nv">names</span><span class="p">[0]</span><span class="k">:-</span><span class="nv">$base</span><span class="si">}</span>
<span class="k">for</span> name <span class="k">in</span> <span class="s2">&quot;</span><span class="si">${</span><span class="nv">names</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">do</span>
    <span class="c1"># 添加前缀和后缀</span>
    <span class="nv">output</span><span class="o">=</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">prefix</span><span class="p">:+</span><span class="si">${</span><span class="nv">prefix</span><span class="si">}</span><span class="nv">_</span><span class="si">}${</span><span class="nv">name</span><span class="si">}${</span><span class="nv">suffix</span><span class="p">:+_</span><span class="si">${</span><span class="nv">suffix</span><span class="si">}}</span><span class="s2">&quot;</span>
    <span class="c1"># 判断是否要全大写输出</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$upper</span> <span class="o">=</span> on <span class="o">]</span>
    <span class="k">then</span>
        <span class="nv">output</span><span class="o">=</span><span class="si">${</span><span class="nv">output</span><span class="p">^^</span><span class="si">}</span>
    <span class="k">fi</span>
    <span class="c1"># 输出结果</span>
    <span class="nb">echo</span> <span class="s2">&quot;</span><span class="nv">$output</span><span class="s2">&quot;</span>
<span class="k">done</span>
$ 
</code></pre></div>

<p>基于这个版本，再使用的时候，需要先输入选项，然后使用双破折线隔开，再输入参数。  </p>
<p>当脚本遇到双破折线时，它会停止处理选项，并将剩下的参数都当作参数：</p>
<div class="highlight"><pre><span></span><code>$ ./format.sh -a after -b befor -u value1 value2 value3
value1 is not an option
$ ./format.sh -a after -b befor -u -- value1 value2 value3
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after -b befor -v -u -- value1 value2 value3
-v is not an option
$ 
</code></pre></div>

<p>第一次没有使用双破折线，所以报错。<br>
第二次正确的用双破折号分隔了参数和选项。<br>
第三次在选项部分出现了未定义的选项，也能发现错误。  </p>
<h5 id="xiao-jie"><a class="toclink" href="#xiao-jie">小结</a></h5>
<p>这一小节的内容也是为下面的getopt命令做铺垫。getopt就是可以帮我们完成命令行参数的解析，返回一个用双破折线隔开选项和参数的规整的参数列表。<br>
另外这里还不支持选项合并：</p>
<div class="highlight"><pre><span></span><code>$ ls -al
</code></pre></div>

<p>这些问题，用getopt都能解决，而且还支持长选项。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>